// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using JetBrains.Annotations;
using SnapObjects.Commons;

namespace SnapObjects.Data.Odbc
{
    internal class OdbcHanaUpdateSqlGenerator : OdbcUpdateSqlGeneratorBase
    {
        public OdbcHanaUpdateSqlGenerator(OdbcHanaSqlGenerationHelper aseSqlGenerationHelper, ITypeMapper typeMapper)
            : base(aseSqlGenerationHelper, typeMapper)
        {
        }

        public override ResultSetMapping AppendRawUpdateSql(
           [NotNull] StringBuilder sqlStringBuilder,
           [NotNull] string rawUpdateSql,
           int sqlPosition)
        {
            sqlStringBuilder.Append(rawUpdateSql);

            if (!rawUpdateSql.TrimEnd().EndsWith(this.SqlGenerationHelper.StatementTerminator))
            {
                sqlStringBuilder.Append(this.SqlGenerationHelper.StatementTerminator);
            }

            sqlStringBuilder.AppendLine();

            return this.AppendSelectAffectedCountSql(sqlStringBuilder, null, null, 0);
        }

        protected override ResultSetMapping AppendSelectAffectedCountSql(
            StringBuilder sqlStringBuilder, string name, string schema, int sqlPosition)
        {
            sqlStringBuilder
                .Append("SELECT ::rowcount as RowCount FROM DUMMY")
                .Append(this.SqlGenerationHelper.StatementTerminator)
                .AppendLine();

            return ResultSetMapping.RowCountAndLastInResultSet;
        }

        protected override void AppendRowsAffectedWhereCondition(StringBuilder sqlStringBuilder,
            int expectedRowsAffected)
        {
            sqlStringBuilder
                .Append("::ROWCOUNT = ")
                .Append(expectedRowsAffected.ToString(CultureInfo.InvariantCulture));
        }

        protected override ResultSetMapping AppendSelectAffectedSql(
           [NotNull] StringBuilder sqlStringBuilder,
           [NotNull] string name,
           [CanBeNull] string schema,
           [NotNull] IReadOnlyList<ColumnModification> readOperations,
           [NotNull] IReadOnlyList<ColumnModification> conditionOperations,
           int sqlPosition)
        {
            Check.NotNull(sqlStringBuilder, nameof(sqlStringBuilder));
            Check.NotEmpty(name, nameof(name));
            Check.NotNull(readOperations, nameof(readOperations));
            Check.NotNull(conditionOperations, nameof(conditionOperations));

            this.AppendSelectHeader(sqlStringBuilder, readOperations);
            this.AppendFromClause(sqlStringBuilder, name, schema);

            this.AppendWhereAffectedClause(sqlStringBuilder, name, schema, conditionOperations);
            
            sqlStringBuilder.Append(this.SqlGenerationHelper.StatementTerminator)
                .AppendLine()
                .AppendLine();

            return ResultSetMapping.SelectAndLastInResultSet;
        }

        private void AppendWhereAffectedClause(
            [NotNull] StringBuilder sqlStringBuilder,
            [NotNull] string name,
            [CanBeNull] string schema,
            [NotNull] IReadOnlyList<ColumnModification> operations)
        {
            Check.NotNull(sqlStringBuilder, nameof(sqlStringBuilder));
            Check.NotNull(operations, nameof(operations));

            sqlStringBuilder.AppendLine().Append("WHERE ");

            this.AppendRowsAffectedWhereCondition(sqlStringBuilder, 1);

            if (operations.Count > 0)
            {
                sqlStringBuilder
                    .Append(" AND ")
                    .AppendValues(operations, (sb, v) =>
                    {
                        if (v.IsKey || v.IsIdentity)
                        {
                            if (v.IsRead)
                            {
                                this.AppendIdentityWhereCondition(sb, name, schema, v);
                            }
                            else
                            {
                                this.AppendWhereAffectedCondition(sb, v, v.UseOriginalValueParameter);
                            }
                        }
                    }, " AND ");
            }
        }

        private void AppendIdentityWhereCondition(
            StringBuilder sqlStringBuilder,
            [NotNull] string name,
            [CanBeNull] string schema, 
            ColumnModification columnModification)
        {
            this.SqlGenerationHelper.DelimitIdentifier(sqlStringBuilder,
                columnModification.MappedColumn);
            sqlStringBuilder.Append(" = ");

            sqlStringBuilder.Append("(SELECT MAX(");
            
            this.SqlGenerationHelper.DelimitIdentifier(sqlStringBuilder, columnModification.MappedColumn);

            sqlStringBuilder.Append(")");

            sqlStringBuilder.Append(" FROM ");

            this.SqlGenerationHelper.DelimitIdentifier(sqlStringBuilder, name, schema);

            sqlStringBuilder.Append(")");
        }
    }
}
